/*
 * Copyright (C) 2005 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <utils/String16.h>

#include <utils/Log.h>
#include <utils/threads.h>

#include "SharedBuffer.h"

namespace android {

    static SharedBuffer *gEmptyStringBuf = NULL;
    static char16_t *gEmptyString = NULL;

    static inline char16_t *getEmptyString() {
        gEmptyStringBuf->acquire();
        return gEmptyString;
    }

    void initialize_string16() {
        SharedBuffer *buf = SharedBuffer::alloc(sizeof(char16_t));
        char16_t *str = (char16_t *) buf->data();
        *str = 0;
        gEmptyStringBuf = buf;
        gEmptyString = str;
    }

    void terminate_string16() {
        SharedBuffer::bufferFromData(gEmptyString)->release();
        gEmptyStringBuf = NULL;
        gEmptyString = NULL;
    }

// ---------------------------------------------------------------------------

    static char16_t *allocFromUTF8(const char *u8str, size_t u8len) {
        if (u8len == 0) return getEmptyString();

        const uint8_t *u8cur = (const uint8_t *) u8str;

        const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len);
        if (u16len < 0) {
            return getEmptyString();
        }

        SharedBuffer *buf = SharedBuffer::alloc(sizeof(char16_t) * (u16len + 1));
        if (buf) {
            u8cur = (const uint8_t *) u8str;
            char16_t *u16str = (char16_t *) buf->data();

            utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1);

            //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
            //printHexData(1, str, buf->size(), 16, 1);
            //printf("\n");

            return u16str;
        }

        return getEmptyString();
    }

// ---------------------------------------------------------------------------

    String16::String16()
            : mString(getEmptyString()) {
    }

    String16::String16(StaticLinkage)
            : mString(0) {
        // this constructor is used when we can't rely on the static-initializers
        // having run. In this case we always allocate an empty string. It's less
        // efficient than using getEmptyString(), but we assume it's uncommon.

        char16_t *data = static_cast<char16_t *>(
                SharedBuffer::alloc(sizeof(char16_t))->data());
        data[0] = 0;
        mString = data;
    }

    String16::String16(const String16 &o)
            : mString(o.mString) {
        SharedBuffer::bufferFromData(mString)->acquire();
    }

    String16::String16(const String16 &o, size_t len, size_t begin)
            : mString(getEmptyString()) {
        setTo(o, len, begin);
    }

    String16::String16(const char16_t *o) {
        size_t len = strlen16(o);
        SharedBuffer *buf = SharedBuffer::alloc((len + 1) * sizeof(char16_t));
        ALOG_ASSERT(buf, "Unable to allocate shared buffer");
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            strcpy16(str, o);
            mString = str;
            return;
        }

        mString = getEmptyString();
    }

    String16::String16(const char16_t *o, size_t len) {
        SharedBuffer *buf = SharedBuffer::alloc((len + 1) * sizeof(char16_t));
        ALOG_ASSERT(buf, "Unable to allocate shared buffer");
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            memcpy(str, o, len * sizeof(char16_t));
            str[len] = 0;
            mString = str;
            return;
        }

        mString = getEmptyString();
    }

    String16::String16(const String8 &o)
            : mString(allocFromUTF8(o.string(), o.size())) {
    }

    String16::String16(const char *o)
            : mString(allocFromUTF8(o, strlen(o))) {
    }

    String16::String16(const char *o, size_t len)
            : mString(allocFromUTF8(o, len)) {
    }

    String16::~String16() {
        SharedBuffer::bufferFromData(mString)->release();
    }

    size_t String16::size() const {
        return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
    }

    void String16::setTo(const String16 &other) {
        SharedBuffer::bufferFromData(other.mString)->acquire();
        SharedBuffer::bufferFromData(mString)->release();
        mString = other.mString;
    }

    status_t String16::setTo(const String16 &other, size_t len, size_t begin) {
        const size_t N = other.size();
        if (begin >= N) {
            SharedBuffer::bufferFromData(mString)->release();
            mString = getEmptyString();
            return NO_ERROR;
        }
        if ((begin + len) > N) len = N - begin;
        if (begin == 0 && len == N) {
            setTo(other);
            return NO_ERROR;
        }

        if (&other == this) {
            LOG_ALWAYS_FATAL("Not implemented");
        }

        return setTo(other.string() + begin, len);
    }

    status_t String16::setTo(const char16_t *other) {
        return setTo(other, strlen16(other));
    }

    status_t String16::setTo(const char16_t *other, size_t len) {
        SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                ->editResize((len + 1) * sizeof(char16_t));
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            memmove(str, other, len * sizeof(char16_t));
            str[len] = 0;
            mString = str;
            return NO_ERROR;
        }
        return NO_MEMORY;
    }

    status_t String16::append(const String16 &other) {
        const size_t myLen = size();
        const size_t otherLen = other.size();
        if (myLen == 0) {
            setTo(other);
            return NO_ERROR;
        } else if (otherLen == 0) {
            return NO_ERROR;
        }

        SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                ->editResize((myLen + otherLen + 1) * sizeof(char16_t));
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            memcpy(str + myLen, other, (otherLen + 1) * sizeof(char16_t));
            mString = str;
            return NO_ERROR;
        }
        return NO_MEMORY;
    }

    status_t String16::append(const char16_t *chrs, size_t otherLen) {
        const size_t myLen = size();
        if (myLen == 0) {
            setTo(chrs, otherLen);
            return NO_ERROR;
        } else if (otherLen == 0) {
            return NO_ERROR;
        }

        SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                ->editResize((myLen + otherLen + 1) * sizeof(char16_t));
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            memcpy(str + myLen, chrs, otherLen * sizeof(char16_t));
            str[myLen + otherLen] = 0;
            mString = str;
            return NO_ERROR;
        }
        return NO_MEMORY;
    }

    status_t String16::insert(size_t pos, const char16_t *chrs) {
        return insert(pos, chrs, strlen16(chrs));
    }

    status_t String16::insert(size_t pos, const char16_t *chrs, size_t len) {
        const size_t myLen = size();
        if (myLen == 0) {
            return setTo(chrs, len);
            return NO_ERROR;
        } else if (len == 0) {
            return NO_ERROR;
        }

        if (pos > myLen) pos = myLen;

#if 0
        printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
               String8(*this).string(), pos,
               len, myLen, String8(chrs, len).string());
#endif

        SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                ->editResize((myLen + len + 1) * sizeof(char16_t));
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            if (pos < myLen) {
                memmove(str + pos + len, str + pos, (myLen - pos) * sizeof(char16_t));
            }
            memcpy(str + pos, chrs, len * sizeof(char16_t));
            str[myLen + len] = 0;
            mString = str;
#if 0
            printf("Result (%d chrs): %s\n", size(), String8(*this).string());
#endif
            return NO_ERROR;
        }
        return NO_MEMORY;
    }

    ssize_t String16::findFirst(char16_t c) const {
        const char16_t *str = string();
        const char16_t *p = str;
        const char16_t *e = p + size();
        while (p < e) {
            if (*p == c) {
                return p - str;
            }
            p++;
        }
        return -1;
    }

    ssize_t String16::findLast(char16_t c) const {
        const char16_t *str = string();
        const char16_t *p = str;
        const char16_t *e = p + size();
        while (p < e) {
            e--;
            if (*e == c) {
                return e - str;
            }
        }
        return -1;
    }

    bool String16::startsWith(const String16 &prefix) const {
        const size_t ps = prefix.size();
        if (ps > size()) return false;
        return strzcmp16(mString, ps, prefix.string(), ps) == 0;
    }

    bool String16::startsWith(const char16_t *prefix) const {
        const size_t ps = strlen16(prefix);
        if (ps > size()) return false;
        return strncmp16(mString, prefix, ps) == 0;
    }

    bool String16::contains(const char16_t *chrs) const {
        return strstr16(mString, chrs) != nullptr;
    }

    status_t String16::makeLower() {
        const size_t N = size();
        const char16_t *str = string();
        char16_t *edit = NULL;
        for (size_t i = 0; i < N; i++) {
            const char16_t v = str[i];
            if (v >= 'A' && v <= 'Z') {
                if (!edit) {
                    SharedBuffer *buf = SharedBuffer::bufferFromData(mString)->edit();
                    if (!buf) {
                        return NO_MEMORY;
                    }
                    edit = (char16_t *) buf->data();
                    mString = str = edit;
                }
                edit[i] = tolower((char) v);
            }
        }
        return NO_ERROR;
    }

    status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) {
        const size_t N = size();
        const char16_t *str = string();
        char16_t *edit = NULL;
        for (size_t i = 0; i < N; i++) {
            if (str[i] == replaceThis) {
                if (!edit) {
                    SharedBuffer *buf = SharedBuffer::bufferFromData(mString)->edit();
                    if (!buf) {
                        return NO_MEMORY;
                    }
                    edit = (char16_t *) buf->data();
                    mString = str = edit;
                }
                edit[i] = withThis;
            }
        }
        return NO_ERROR;
    }

    status_t String16::remove(size_t len, size_t begin) {
        const size_t N = size();
        if (begin >= N) {
            SharedBuffer::bufferFromData(mString)->release();
            mString = getEmptyString();
            return NO_ERROR;
        }
        if ((begin + len) > N) len = N - begin;
        if (begin == 0 && len == N) {
            return NO_ERROR;
        }

        if (begin > 0) {
            SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                    ->editResize((N + 1) * sizeof(char16_t));
            if (!buf) {
                return NO_MEMORY;
            }
            char16_t *str = (char16_t *) buf->data();
            memmove(str, str + begin, (N - begin + 1) * sizeof(char16_t));
            mString = str;
        }
        SharedBuffer *buf = SharedBuffer::bufferFromData(mString)
                ->editResize((len + 1) * sizeof(char16_t));
        if (buf) {
            char16_t *str = (char16_t *) buf->data();
            str[len] = 0;
            mString = str;
            return NO_ERROR;
        }
        return NO_MEMORY;
    }

}; // namespace android
